package com.webserver.http;

import java.io.*;
import java.net.Socket;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 作者：JustOne
 * 微信：Andylau2020smile
 * 抖音：JustOne2018
 * 日期：2022/6/14 14:06
 */
public class MyHttpResponse {
    private Socket socket;//与当前客户对应的服务端socket，类似于讲师，由MyClientHandler传过来
    private OutputStream os;//通过socket获取的输出流，用于向浏览器写出

    //part1:【状态行】相关属性
    private int statusCode = 200;//状态码
    private String statusReason = "OK";//状态描述

    //part2:【响应头】相关属性。。key=响应头的名字, value=响应头的值，例如：Content-Type: text/heml
    private Map<String , String> headersMap = new HashMap<>();

    //part3:【响应体】相关属性
    private File file;  //响应正文，由ClientHandler传进来
    private byte[] data;//响应正文，动态拼成的。
    private ByteArrayOutputStream baos;//也可以通过这个流写出的所有字节作为正文

    ////////构造方法，为属性赋值
    public MyHttpResponse(Socket socket){
        try {
            this.socket = socket;
            this.os = socket.getOutputStream();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    ////////将当前响应对像以HTTP标准响应格式发送给客户
    public void flush() throws IOException {
        System.out.println("MyHttpResponse:开始发送响应...");
        sendStatusLine();//part1:【状态行】的发送
        sendHeaders();   //part2:【响应头】的发送
        sendContent();   //part3:【响应体】的发送
        System.out.println("MyHttpRresponse:响应发送完毕。");
    }

    ////////part1:【状态行】的发送  [ˈsteɪtəs]
    private void sendStatusLine() throws IOException {
        //---a,状态行
        //原来：String line = "HTTP/1.1 200 OK";//记得有空格
        writeLine("HTTP/1.1 "+this.statusCode+" " + this.statusReason);
    }

    ////////part2:【响应头】的发送
    private void sendHeaders() throws IOException {
        //以前学过如何遍历Map的每组键值对，方法是：
        //将Map中每组键值对以若干Entry实例存入Set集合进行返回
        Set<Map.Entry<String,String>> entrySet = headersMap.entrySet();
        for(Map.Entry<String,String> header : entrySet){
            String line = header.getKey() + ": " + header.getValue();
            writeLine(line);
        }
        //原来是这样的，留着看
        //writeLine("Content-Type: text/html");
        //writeLine("Content-Length: "+file.length());
        writeLine("");//其实就相当于只发送了13,10回车换行
    }


    ////////part3:【响应体】的发送 [ˈkɒntent]
    private void sendContent() throws IOException {
        //如果 baos字节数组中有数据，就说明需有动态数据要响应对浏览器，就设置给data
        if(baos != null){
            data = baos.toByteArray();//把baos流转成字节数组(本次是把二维码转成字节数组)
            putHeader("Content-Length",data.length+"");//设置正文长度
        }


        //如果 [事先写好的正文文件] 不为空就发迸
        if(file != null) {
            try (FileInputStream fis = new FileInputStream(file)) {
                int len = -1;
                byte[] data = new byte[1024 * 10];
                while ((len = fis.read(data)) != -1) {
                    os.write(data, 0, len);
                }
            }
        }

        //如果 [动态拼成的正文] 不为空，就发送
        //二选一，请求上else
        else if(data != null){
            try {
                os.write(data);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    ////////写一行 //write [raɪt]
    private void writeLine(String line) throws IOException {
        os.write(line.getBytes("ISO8859-1"));
        os.write(13);
        os.write(10);
    }

    ////////---get set-----------------------------------------------------------------
    public File getFile(){
        return file;
    }

    public void setFile(File file){
        //设置响应头。。范传奇说这两个头放到这里最合理，因为他两是用于说明正文的，有正文才需要，所以放到setFile中

        //如果分析出结果，就设置给响应头以说明响应数据的类型
        //如果没有分析出结果，就不发送Content-Type这个头了，HTTP协议规定服务端不发送这个头就由浏览器自行判断正文类型
        //Files的该方法用于分析参数传入的文件对应的Content-Type的值
        String contentType = null;
        try {
            contentType = Files.probeContentType(file.toPath());
            putHeader("Content-Type",contentType);
        } catch (IOException e) {
            e.printStackTrace();
        }

        putHeader("Content-Length",file.length()+"");
        this.file = file;
    }

    //设置正文  和  响应头   --- 动态生成的页面
    public void setData(byte[] data){
        this.data = data;
        putHeader("Content-Type","text/html");        //设置响应头，即为map赋值
        putHeader("Content-Length", data.length+"");  //设置响应头，即为map赋值
    }

    //状态码
    public int getStatusCode() {
        return statusCode;
    }

    public void setStatusCode(int statusCode) {
        this.statusCode = statusCode;
    }

    //状态描述
    public String getStatusReason() {
        return statusReason;
    }

    public void setStatusReason(String statusReason) {
        this.statusReason = statusReason;
    }

    //响应头。。这个是手动敲的，用于对响应头 加值 和 取值
    public void putHeader(String name, String value){
        headersMap.put(name, value);
    }

    public String getHeaderValue(String name){
        return headersMap.get(name);//不要返回集合给外界，避免被修改
    }


    //////要求客户端重定向到指定位置，状态代码为302。响应头:Location用于指定重定向路径
    public void sendRedirect(String uri){
        statusCode = 302;
        statusReason = "Moved Temporarily";
        putHeader("Location",uri);//Location [ləʊˈkeɪʃn] 位置
    }

    //////设置响应头 之 文件类型
    public void setContentType(String value){
        putHeader("Content-Type",value);
    }

    //////获取一个 字节数组输出流
    public OutputStream getBaos(){
        if(baos == null){
            baos = new ByteArrayOutputStream();
        }
        return this.baos;
    }
}



















